
(function(){

  var namespace = 'App.Component';

  //
  // import names
  //

  var classList = Basis.CSS.classList;

  var nsData = Basis.Data;
  var nsWrapper = Basis.DOM.Wrapper;
  var nsEntity = Basis.Entity;

  var KEY = Basis.Event.KEY;

  //
  // helpers
  //

  function runCommand(command){
    // store command
    App.Type.CommandRun({
      text: command,
      lastRun: new Date
    });

    // run command 
    try {
      App.parser.run(command, true);
    } catch(e) {
      if (typeof e == 'string')
        return e;
      App.parser.run(command);
    }
  }

  //
  // Main part
  //

  var suggestDataSource = new nsData.Dataset.Subset({
    source: App.Type.CommandRun.all
  });
  var suggestFitler = new nsData.Property.Property('', {
    change: function(value){
      suggestDataSource.setRule(function(item){
        return item.data.text != value && item.data.text.indexOf(value) == 0;
      });
    }
  }, String.trim);

  var consolePanel = new nsWrapper.TmplNode({
    template:
      '<div id="Console">' +
        '<div class="ToggleButton"/>' +
        '<div class="InputWrapper">' +
          '<span{context} class="ContextLocation">{contextText}</span>' +
          '<input{input} type="text"' +
            ' event-keydown="keydown"' +
            ' event-keyup="keyup"' +
            ' event-change="change"' +
            ' event-focus="showsuggest"' +
            ' event-blur="hidesuggest"/>' +
        '</div>' +
      '</div>',

    action: {
      showsuggest: function(){
        suggestPopup.adaptiveShow();
      },
      hidesuggest: function(){
        suggestPopup.hide();
        errorPopup.hide();
      }      
    },

    templateAction: function(actionName, event){
      var input = this.tmpl.input;
      var eventKey = Basis.Event.key(event);
      var suggest = suggestPopup.selection.pick();

      if (actionName == 'keydown')
      {
        switch (eventKey){
          case KEY.UP:
          case KEY.DOWN:
            Basis.Event.cancelDefault(event);
          break;
          case KEY.DELETE:
            if (suggest)
            {
              suggest.target.destroy();
              Basis.Event.cancelDefault(event);
            }
          break;
        }
      }

      if (actionName == 'keyup')
      {
        switch (eventKey){
          case KEY.ENTER:
          case KEY.CTRL_ENTER:
            if (suggest)
            {
              suggest.unselect();
              input.value = suggest.data.text;
            }
            else
            {
              var error = runCommand(input.value);
              input.value = '';
              if (error)
              {
                suggestPopup.hide();
                errorPopup.update({
                  message: error
                });
                errorPopup.show();
                return;
              }
            }
          break;

          case KEY.UP:
            if (suggestPopup.visible)
              (suggest ? suggest.previousSibling || suggest : suggestPopup.lastChild).select();
          break;

          case KEY.DOWN:
            if (suggestPopup.visible)
              (suggest ? suggest.nextSibling || suggest : suggestPopup.firstChild).select();
          break;

          default:
            if (suggest)
              suggest.unselect();
        }
      }

      if (actionName == 'keyup' || actionName == 'change')
      {
        errorPopup.hide();
        suggestFitler.set(input.value);
        suggestPopup.adaptiveShow(suggestPopup.selection.itemCount || (actionName == 'keyup' && eventKey == KEY.UP));
      }

      nsWrapper.TmplNode.prototype.templateAction.call(this, actionName, event);
    },

    init: function(config){
      this.constructor.prototype.init.call(this, config);
      var self = this;
      this.context = new Basis.Data.Property.Property(null, {
        change: function(value){
          self.tmpl.contextText.nodeValue = value;
          self.tmpl.input.style.paddingLeft = self.tmpl.context.offsetWidth + 'px';
          if (suggestPopup)
            suggestPopup.element.style.marginLeft = (self.tmpl.context.offsetWidth - 4) + 'px';
        }
      }, function(value){ return '~/' + (value ? value + '/' : value); });
    },

    focus: function(){
      Basis.DOM.focus(this.tmpl.input);
    }
  });

  var consoleLog = new nsWrapper.TmplContainer({
    container: consolePanel.element,
    id: 'ConsoleLog',
    dataSource: App.Type.CommandResult.all,
    localSorting: Function.getter('data.id'),
    localSortingDesc: true,
    childClass: {
      template:
        '<div class="ConsoleLog-Item"/>',

      handler: {
        update: function(){
          classList(this.element).add(this.data.type);
          this.element.innerHTML = this.data.content;
        }
      }
    }
  });

  var suggestPopup = new Basis.Controls.Popup.Popup({
    id: 'CommandSuggestPopup',
    dir: 'left top left bottom',
    hideOnAnyClick: false,
    dataSource: suggestDataSource,
    selection: true,
    localSorting: Function.getter('data.lastRun', Number),
    childClass: {
      template: 
        '<div class="CommandSuggest-Item" event-mousedown="kill" event-click="select">' +
          '{suggestText}' +
        '</div>',

      handler: {
        update: function(){
          this.tmpl.suggestText.nodeValue = this.data.text;
        }
      },

      action: {
        kill: function(event){
          Basis.Event.cancelDefault(event);
        },
        select: function(event){
          var input = consolePanel.tmpl.input;
          input.value = this.data.text;
          consolePanel.templateAction('change');
        }
      }
    },
    relElement: consolePanel.tmpl.input,
    adaptiveShow: function(evenEmpty){
      if ((evenEmpty || suggestFitler.value) && this.firstChild)
        this.show();
      else
        this.hide();
    }
  });

  var errorPopup = new Basis.Controls.Popup.Balloon({
    id: 'CommandErrorBalloon',
    dir: 'left top left bottom',
    relElement: consolePanel.tmpl.input,
    childNodes: [
      new nsWrapper.TmplNode({
        autoDelegateParent: true,
        template:
          '<div>' +
            '<p>{messageText}</p>' +
          '</div>',

        handler: {
          update: function(){
            this.tmpl.messageText.nodeValue = this.data.message;
          }
        }
      })
    ]
  });

  //
  // bind to UrlResolver
  //
  var setConsoleFocusTimer;
  App.UrlResolver.addHandler({
    targetChanged: function(path){
      var object = App.Type.resolveBem(path);

      clearTimeout(setConsoleFocusTimer);

      if (object && object.type !== App.Type.Tech)
        setConsoleFocusTimer = setTimeout(function(){
          consolePanel.focus()
        }, 0);
    },
    contextChanged: function(context){
      consolePanel.context.set(context);
    }
  });

  //
  // export names
  //

  Basis.namespace(namespace).extend({
    Console: consolePanel
  });

})();